package com.bluetoothsms.android;

import java.io.IOException;
import java.lang.ref.WeakReference;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.Layout;
import android.text.method.ScrollingMovementMethod;
import android.widget.TextView;

import com.android.bluetoothsms.R;
import com.bluetoothsms.common.Common;
import com.bluetoothsms.common.Common.ConnectionState;
import com.bluetoothsms.common.MessageSignal;
import com.bluetoothsms.common.Sms;

public class MainActivity extends Activity {

	public final static String ACTION_SMS_SENT = "SmsSent";
	public final static String ACTION_SMS_DELIVERED = "SmsDelivered";

	private final static int REQUEST_ENABLE_BT = 1;

	private static TextView txtSMS;
	private static ConnectionThread connectionThread;

	private ServerThread mListeningThread;
	private Handler mHandler = new ThreadHandler(this);
	private ConnectionState mConnectionState;
	private MessageSignal mLastSignalMsg;
	private PhoneStateListener mPhoneStateListener;
	private TelephonyManager mTelephonyManager;
	private BroadcastReceiver mBtReceiver;
	private BroadcastReceiver mSmsSentReceiver;
	private BroadcastReceiver mSmsDeliveredReceiver;
	private BluetoothAdapter mBtAdapter;

	private static class ThreadHandler extends Handler {

		private final WeakReference<MainActivity> mActivity;

		public ThreadHandler(MainActivity activity) {
			mActivity = new WeakReference<MainActivity>(activity);
		}

		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case Common.MSG_CONNECTED:
				mActivity.get().onConnected();
				break;

			case Common.MSG_CONNECTION_CLOSED:
				mActivity.get().onConnectionClosed();
				break;

			case Common.MSG_CONNECTION_FAILED:
				mActivity.get().onConnectionFailed();
				break;
			}
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		txtSMS = (TextView) findViewById(R.id.txtSMS);
		txtSMS.setText("");
		txtSMS.setMovementMethod(new ScrollingMovementMethod());

		mConnectionState = ConnectionState.Disconnected;
		setConnectionState(ConnectionState.Disconnected);

		createSignalListener();
		createBtListener();
		createSmsSentListener();
		createSmsDeliveredListener();
		enableBt();
	}

	@Override
	protected void onDestroy() {
		if (mTelephonyManager != null && mPhoneStateListener != null) {
			mTelephonyManager.listen(mPhoneStateListener,
					PhoneStateListener.LISTEN_NONE);
		}

		if (mBtReceiver != null)
			unregisterReceiver(mBtReceiver);

		if (mSmsSentReceiver != null)
			unregisterReceiver(mSmsSentReceiver);

		if (mSmsDeliveredReceiver != null)
			unregisterReceiver(mSmsDeliveredReceiver);

		disconnect();

		super.onDestroy();
	}

	private void createSignalListener() {
		mPhoneStateListener = new PhoneStateListener() {

			@Override
			public void onSignalStrengthsChanged(SignalStrength signalStrength) {

				mLastSignalMsg = new MessageSignal(
						signalStrength.getGsmSignalStrength());

				if (connectionThread != null)
					connectionThread.offerMessage(mLastSignalMsg);

				super.onSignalStrengthsChanged(signalStrength);
			}

		};

		mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		mTelephonyManager.listen(mPhoneStateListener,
				PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
	}

	private void createBtListener() {
		mBtReceiver = new BroadcastReceiver() {

			@Override
			public void onReceive(Context context, Intent intent) {
				int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
						BluetoothAdapter.ERROR);

				switch (state) {
				case BluetoothAdapter.STATE_ON:
					updateMessageBox("Bluetooth adapter was enabled.");
					listen();
					break;

				case BluetoothAdapter.STATE_OFF:
					updateMessageBox("Bluetooth adapter was disabled.");
					break;

				case BluetoothAdapter.STATE_TURNING_OFF:
					disconnect();
					updateMessageBox("Bluetooth adapter is being disabled.");
					break;

				case BluetoothAdapter.STATE_TURNING_ON:
					updateMessageBox("Bluetooth adapter is being enabled.");
					break;
				}
			}
		};

		IntentFilter filter = new IntentFilter(
				BluetoothAdapter.ACTION_STATE_CHANGED);
		registerReceiver(mBtReceiver, filter);
	}

	private void createSmsSentListener() {
		mSmsSentReceiver = new BroadcastReceiver() {

			@Override
			public void onReceive(Context context, Intent intent) {
				int resultCode = getResultCode();

				switch (resultCode) {
				case Activity.RESULT_OK:
					updateMessageBox("SMS sent.");
					break;
				case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
					updateMessageBox("SMS NOT sent: Generic failure!");
					break;
				case SmsManager.RESULT_ERROR_NO_SERVICE:
					updateMessageBox("SMS NOT sent: No service!");
					break;
				case SmsManager.RESULT_ERROR_NULL_PDU:
					updateMessageBox("SMS NOT sent: Null PDU!");
					break;
				case SmsManager.RESULT_ERROR_RADIO_OFF:
					updateMessageBox("SMS NOT sent: Radio off!");
					break;
				}

				if (connectionThread != null) {
					connectionThread.onSmsSent(resultCode);
				}
			}
		};

		IntentFilter filter = new IntentFilter(ACTION_SMS_SENT);
		registerReceiver(mSmsSentReceiver, filter);
	}

	private void createSmsDeliveredListener() {
		mSmsDeliveredReceiver = new BroadcastReceiver() {

			@Override
			public void onReceive(Context context, Intent intent) {
				boolean success = false;

				switch (getResultCode()) {
				case Activity.RESULT_OK:
					updateMessageBox("SMS delivered.");
					success = true;
					break;
				case Activity.RESULT_CANCELED:
					updateMessageBox("SMS NOT delivered!");
					break;
				}

				if (connectionThread != null) {
					connectionThread.onSmsDelivered(success);
				}
			}
		};

		IntentFilter filter = new IntentFilter(ACTION_SMS_DELIVERED);
		registerReceiver(mSmsDeliveredReceiver, filter);
	}

	private void enableBt() {
		mBtAdapter = BluetoothAdapter.getDefaultAdapter();

		if (mBtAdapter != null) {
			if (!mBtAdapter.isEnabled()) {
				updateMessageBox("Bluetooth adapter is disabled.");
				Intent enableBtIntent = new Intent(
						BluetoothAdapter.ACTION_REQUEST_ENABLE);
				startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
			} else {
				updateMessageBox("Bluetooth adapter is already enabled.");
				listen();
			}
		} else {
			updateMessageBox("No bluetooth adapter found!");
		}
	}

	private void disconnect() {
		// Threads beenden
		if (connectionThread != null) {
			connectionThread.cancel();
			connectionThread = null;
		}

		if (mListeningThread != null) {
			mListeningThread.cancel();
			mListeningThread = null;
		}

		setConnectionState(ConnectionState.Disconnected);
	}

	private static void updateMessageBox(String msg) {
		txtSMS.append(msg + "\n");

		// Ans Ende scrollen
		final Layout layout = txtSMS.getLayout();
		if (layout != null) {
			int scrollDelta = layout.getLineBottom(txtSMS.getLineCount() - 1)
					- txtSMS.getScrollY() - txtSMS.getHeight();
			if (scrollDelta > 0)
				txtSMS.scrollBy(0, scrollDelta);
		}
	}

	public static void onSmsReceived(Sms sms) {
		updateMessageBox("Incoming SMS.");

		if (connectionThread != null)
			connectionThread.onSmsReceived(sms);
	}

	private void setConnectionState(ConnectionState state) {
		switch (state) {
		case Connecting:
			updateMessageBox("Listening for incoming connection...");
			break;

		case Disconnected:
			if (mConnectionState.equals(ConnectionState.Connecting)) {
				updateMessageBox("Listening stopped.");
			} else {
				updateMessageBox("Disconnected.");
			}
			break;

		case Connected:
			updateMessageBox("Connected.");
			break;
		}

		mConnectionState = state;
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == REQUEST_ENABLE_BT && resultCode != RESULT_OK) {
			updateMessageBox("Bluetooth adapter was not enabled!");
		}
	}

	private void listen() {
		if (mBtAdapter != null) {
			setConnectionState(ConnectionState.Connecting);
			int tries = 0;

			// Maximal zweimal versuchen
			while (tries < 2) {
				try {
					mListeningThread = new ServerThread(mBtAdapter, mHandler);
					mListeningThread.start();
					// Kein Fehler aufgetreten, Schleife abbrechen
					tries = 2;
				} catch (IOException e) {
					updateMessageBox("Failed to start listening:");
					updateMessageBox(e.getMessage());
					tries++;

					if (tries < 2) {
						// Nochmal versuchen
						updateMessageBox("Retrying...");
					} else {
						// Listening abbrechen
						updateMessageBox("Aborting.");
						disconnect();
					}
				}
			}
		}
	}

	private void onConnected() {
		if (mConnectionState.equals(ConnectionState.Connecting)) {
			BluetoothSocket socket = mListeningThread.getSocket();

			if (socket != null) {
				updateMessageBox("Connected to: "
						+ socket.getRemoteDevice().getName() + " ("
						+ socket.getRemoteDevice().getAddress() + ")");
				setConnectionState(ConnectionState.Connected);
				connectionThread = new ConnectionThread(socket, mHandler, this);
				connectionThread.offerMessage(mLastSignalMsg);
				connectionThread.start();
			} else {
				updateMessageBox("Failed to establish connection:");
				updateMessageBox(mListeningThread.getErrorMessage());
				disconnect();
			}
		}
	}

	private void onConnectionClosed() {
		if (mConnectionState.equals(ConnectionState.Connected)) {

			// Listening Server wieder starten
			updateMessageBox("Connection timed out!");
			disconnect();
			listen();
		}
	}

	private void onConnectionFailed() {
		// Wird aufgerufen, wenn beim Verbindungsaufbau ein Fehler auftritt
		// (im ServerThread)
		if (mConnectionState.equals(ConnectionState.Connecting)) {

			updateMessageBox("Failed to establish connection:");
			updateMessageBox(mListeningThread.getErrorMessage());

			disconnect();
			listen();
		}
	}
}
